﻿namespace wwm.LeetCodeHelper;

/// <summary>
/// 反射相关
/// </summary>
public class TestResultHelper
{
    private static JsonSerializerOptions _jsonSerializerOption = new JsonSerializerOptions()
    {
        //Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(UnicodeRanges.All)
        Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
    };
    //static string path = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "";
    /// <summary>
    /// 开始测试
    /// <para>需要传入当前项目命名空间名称及dll文件名称,如:typeof(Program).Assembly.GetName().Name</para>
    /// </summary>
    /// <param name="testClassNameEndFlag"></param>
    public static void InvokeAllTest(string testClassNameEndFlag = "Solutions")
    {
        //string currentDllName = typeof(Program).Assembly.GetName().Name;
        //string currentDllPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + currentDllName + ".dll";
        string currentDllPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + AppDomain.CurrentDomain.FriendlyName + ".dll";
        Assembly assembly = Assembly.LoadFrom(currentDllPath);
        Type[] ts = assembly.GetTypes();
        //仅执行继承了ITest的类
        var assignClassList = ts.Where(t => !t.IsAbstract && t.IsClass && typeof(ITest).IsAssignableFrom(t));
        if (assignClassList.Count() == 0)
        {
            //如果没有ITest则不执行
            ConsoleHelper.Warn("没有可执行类,测试类需要继承自:" + nameof(ITest));
            return;
        }
        Stopwatch stopwatch = new Stopwatch();
        foreach (Type a in assignClassList)
        {

            ConsoleHelper.Write("------->" + a.Name, ConsoleColor.Gray);
            Console.WriteLine();
            //创建对象
            var instance = Activator.CreateInstance(a);
            //获取对象下的所有方法
            var methods = a.GetMethods().Where(m => m.IsDefined(typeof(DataAttribute), true)).ToList();
            stopwatch.Start();
            //测试总数
            int all_test_num = 0;
            //测试成功个数
            int success_test_num = 0;
            foreach (MethodInfo method in methods)
            {
                //获取方法上的所有InlineDataAttribute特性
                //var inlineAttributes = method.GetCustomAttributes<InlineDataAttribute>() as IEnumerable<DataAttribute>;
                //foreach (var inline in inlineAttributes)
                //{
                //    dataList.AddRange(inline.GetData(method));
                //}
                var theoryAttributes = method.GetCustomAttributes<DataAttribute>(false);
                List<object[]> dataList = new List<object[]>();
                List<bool> ignoreOrderList = new List<bool>();
                foreach (var theory in theoryAttributes)
                {
                    var datas = theory.GetData();
                    if (datas == null || datas.All(d => d == null)) continue;
                    dataList.AddRange(datas);
                    ignoreOrderList.AddRange(Enumerable.Range(0, datas.Count()).Select(a => theory.IgnoreOrder));
                }

                //获取inlineData参数
                for (int i = 0; i < dataList.Count; i++)
                {
                    all_test_num++;
                    object[] data = dataList[i];
                    try
                    {
                        int parameterLength = method.GetParameters().Length;
                        if (data.Length != parameterLength + 1)
                        {
                            ConsoleHelper.Error(method.Name + ": 参数个数错误");
                        }
                        else
                        {
                            var paramList = data[..parameterLength];
                            var res = method.Invoke(instance, paramList);

                            if (method.ReturnType.Name == "Void")
                            {
                                //比较method的第一个参数
                                res = paramList[0];//TODO: 没有作为引用将数据修改,待修正
                            }
                            var lastParam = data[^1];
                            if (CompareTwoResult(res, lastParam, ignoreOrderList[i]))
                            {
                                if (!IsValueType(res))
                                {
                                    //优先使用自定义ToString方法
                                    if (((TypeInfo)res.GetType()).DeclaredMethods.FirstOrDefault(p => p.Name == "ToString") != null)
                                    {
                                        res = JsonSerializer.Serialize(res.ToString(), _jsonSerializerOption);
                                    }
                                    else res = JsonSerializer.Serialize(res, _jsonSerializerOption);
                                }
                                ConsoleHelper.Success(method.Name + ": 执行结果正确: " + res);
                                success_test_num++;
                            }
                            else
                            {
                                if (!IsValueType(res) || !IsValueType(lastParam))
                                {
                                    //优先使用自定义ToString方法
                                    if (res != null && ((TypeInfo)res.GetType()).DeclaredMethods.FirstOrDefault(p => p.Name == "ToString") != null)
                                    {
                                        res = JsonSerializer.Serialize(res.ToString(), _jsonSerializerOption);
                                    }
                                    else
                                    {
                                        res = JsonSerializer.Serialize(res, _jsonSerializerOption);
                                    }
                                    if (lastParam != null && ((TypeInfo)lastParam.GetType()).DeclaredMethods.FirstOrDefault(p => p.Name == "ToString") != null)
                                    {
                                        lastParam = JsonSerializer.Serialize(lastParam.ToString(), _jsonSerializerOption);
                                    }
                                    else
                                    {
                                        lastParam = JsonSerializer.Serialize(lastParam, _jsonSerializerOption);
                                    }
                                }
                                ConsoleHelper.Error(method.Name + ": 执行结果错误. " + Environment.NewLine + "  预期值:" + lastParam + Environment.NewLine + "  结果值:" + res);
                            }
                        }
                    }
                    catch (Exception ex)
                    {

                        ConsoleHelper.Error(ex.Message + ",innerException:" + ex.InnerException);
                    }
                }
            }
            stopwatch.Stop();
            //带有测试正确个数的输出结果
            ConsoleHelper.Write("_______测试结果: ", ConsoleColor.Green);
            ConsoleHelper.Write(success_test_num, success_test_num < all_test_num ? ConsoleColor.Red : ConsoleColor.Green);
            ConsoleHelper.Write("/" + all_test_num, ConsoleColor.Green);
            ConsoleHelper.Write(" ,耗时:" + stopwatch.ElapsedMilliseconds + "ms________", ConsoleColor.Green);
            Console.WriteLine(Environment.NewLine);
        }
    }

    /// <summary>
    /// 比较结果
    /// </summary>
    /// <param name="obj1"></param>
    /// <param name="obj2"></param>
    /// <param name="ignoreOrder"></param>
    /// <returns></returns>
    public static bool CompareTwoResult(object obj1, object obj2, bool ignoreOrder = false)
    {
        if (obj1 == null && obj2 == null) return true;
        else if (obj1 == null || obj2 == null) return false;
        var obj1Type = obj1.GetType();
        var obj2Type = obj2.GetType();

        if (IsValueType(obj1))
        {
            if (obj1Type.Name != obj2Type.Name)
            {
                return obj1.ToString() == obj2.ToString();
            }
            return obj1.Equals(obj2);
        }

        if (obj1Type.Name != obj2Type.Name)
        {
            if ((obj1Type.Name.StartsWith("List") || obj1Type.Name.EndsWith("[]")) && (obj2Type.Name.StartsWith("List") || obj2Type.Name.EndsWith("[]")))
            {
                //如果两个列表都是List开头的则可以继续使用List的方法操作
            }
            else return false;
        }
        if (obj1Type.IsGenericType || obj1Type.IsArray)
        {
            var do1 = (dynamic)obj1;
            var do2 = (dynamic)obj2;
            int ol1 = obj1Type.IsGenericType ? do1.Count : do1.Length;
            int ol2 = obj2Type.IsGenericType ? do2.Count : do2.Length;
            if (ol1 != ol2) return false;
            else if (ol1 == 0) return true;
            //排序,TODO: 最外层也要排序
            //if (ignoreOrder)
            //{
            //    Array.Sort(do1.ToArray());
            //    Array.Sort(do2.ToArray());
            //}
            var c1 = ((dynamic)obj1)[0];
            var c2 = ((dynamic)obj2)[0];
            if (IsValueType(c1))
            {
                if (ignoreOrder)
                {
                    if (obj1Type.IsGenericType) do1.Sort();
                    else if (obj1Type.IsArray) Array.Sort(do1);
                    if (obj2Type.IsGenericType) do2.Sort();
                    else if (obj2Type.IsArray) Array.Sort(do2);
                }
                bool isEqual = true;
                for (int i = 0; i < ol1; i++)
                {
                    if (do1[i] != do2[i])
                    {
                        isEqual = false;
                        break;
                    }
                }
                return isEqual;
            }
            List<bool> isAllTrue = new List<bool>(ol1);
            for (int i = 0; i < ol1; i++)
            {
                isAllTrue.Add(CompareTwoResult(do1[i], do2[i], ignoreOrder));
            }
            return isAllTrue.All(p => p == true);
        }
        else if (obj1Type.IsClass)
        {
            //var props1 = obj1Type.GetProperties();
            //var props2 = obj2Type.GetProperties();

            var props1 = obj1Type.GetFields();
            var props2 = obj2Type.GetFields();

            List<bool> isAllTrue = new(props1.Length);
            //比较相同属性的值是否相等
            for (int i = 0; i < props1.Length; i++)
            {
                var p1 = props1[i];
                var p2 = props2[i];
                isAllTrue.Add(CompareTwoResult(p1.GetValue(obj1), p2.GetValue(obj2), ignoreOrder));
            }
            return isAllTrue.All(p => p == true);
        }
        return false;
    }

    private static bool IsValueType(object obj)
    {
        return obj == null || obj.GetType().IsValueType || obj.GetType().Name == "String" || obj.GetType().BaseType.IsValueType;
    }
}
